﻿using Microsoft.AspNetCore.Authentication;
using Microsoft.Extensions.Primitives;
using OpenIddict.Abstractions;
using static OpenIddict.Abstractions.OpenIddictConstants;
using System.Security.Claims;

namespace openiddict.demo
{
    public class AuthorizationService
    {
        // 解析 OAuth 请求参数 (Form or Query)
        public IDictionary<string, StringValues> ParseOAuthParameters(HttpContext httpContext, List<string>? excluding = null)
        {
            excluding ??= new List<string>();
            var parameters = new Dictionary<string, StringValues>();

            if (httpContext.Request.HasFormContentType)
            {
                parameters = httpContext.Request.Form
                    .Where(pair => !excluding.Contains(pair.Key))
                    .ToDictionary(pair => pair.Key, pair => pair.Value);
            }
            else
            {
                parameters = httpContext.Request.Query
                    .Where(pair => !excluding.Contains(pair.Key))
                    .ToDictionary(pair => pair.Key, pair => pair.Value);
            }
            return parameters;
        }


        /// <summary>
        /// 构建重定向 URL
        /// </summary>
        /// <param name="request"></param>
        /// <param name="oAuthParameters"></param>
        /// <returns></returns>
        public string BuildRedirectUrl(HttpRequest request, IDictionary<string, StringValues> oAuthParameters)
        {
            var queryString = QueryString.Create(oAuthParameters);
          
            // Combine PathBase, Path, and the new QueryString
            var url = request.PathBase + request.Path + queryString;
            return url;
        }


        // 检查用户认证状态和 MaxAge
        public bool IsUserAuthenticated(AuthenticateResult authenticateResult, OpenIddictRequest request)
        {
            if (!authenticateResult.Succeeded || authenticateResult.Principal == null)
            {
                return false;
            }

            // Check MaxAge
            if (request.MaxAge.HasValue && authenticateResult.Properties?.IssuedUtc != null)
            {
                var maxAgeSeconds = TimeSpan.FromSeconds(request.MaxAge.Value);
                var authenticationDate = authenticateResult.Properties.IssuedUtc.Value;

                if (DateTimeOffset.UtcNow - authenticationDate > maxAgeSeconds)
                {
                    return false; // Authentication is too old
                }
            }

            return true;
        }


        // 决定声明的目标 (Destination)
        public static IEnumerable<string> GetDestinations(ClaimsIdentity identity, Claim claim)
        {
            //默认情况下，声明既不会在访问令牌中发出，也不会在身份令牌中发出。
            //仅当“openid”作用域被授予时，才要求OpenIddict在身份令牌中发出声明
            //以及与该声明对应的用户控制器是否被列为OIDC声明。
            if (claim.Type is Claims.Name or Claims.Email or Claims.Role)
            {
                yield return Destinations.AccessToken;

                if (identity.HasScope(Scopes.OpenId))
                {
                    // Only add to ID token if 'openid' scope is present
                    if (claim.Type is Claims.Name && identity.HasScope(Scopes.Profile))
                        yield return Destinations.IdentityToken;

                    if (claim.Type is Claims.Email && identity.HasScope(Scopes.Email))
                        yield return Destinations.IdentityToken;

                    if (claim.Type is Claims.Role && identity.HasScope(Scopes.Roles))
                        yield return Destinations.IdentityToken;
                }
            }

            //永远不要在访问令牌中包含安全戳，因为它是一个秘密值。
            else if (claim.Type is "aspnet.identity.securitystamp")
            {
                yield break;
            }
            else
            {
                //默认行为：如果授予了相关作用域，则添加到访问令牌中
                if (identity.HasScope(Scopes.Profile) && claim.Type is Claims.PreferredUsername or Claims.GivenName or Claims.FamilyName)
                    yield return Destinations.AccessToken;

                //如果需要，根据范围添加其他声明以访问令牌
                //示例：if（identity.HasScope（“custom_scope”）&&claims。类型==“my_custom_claim”）返回目的地。AccessToken；

                //默认情况下，声明不会添加到身份令牌中。
                //要向身份令牌添加声明，请确保授予了“openid”范围
                //并将索赔类型映射到标准OIDC索赔或定义自定义范围。
            }
        }

    }
}
